package org.kth.dks.dks_marshal;

/**
 * <p>Title: DKS</p>
 * <p>Description: DKS Middleware</p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: KTH-IMIT</p>
 * @author not attributable
 * @version 1.0
 */

import java.io.IOException;
import java.util.Vector;

import org.kth.dks.DKSObject;
import org.kth.dks.dks_comm.DKSRef;
import org.kth.dks.dks_node.LookupType;

public class LookupMsg
    extends DKSMessage implements ForwardedMsgInterface { // shouldnt be public!
  private static String NAME = "LOOKUP";

  private long target;
  private long intervalStart;
  private LookupType type;
  private Vector stack = new Vector();
  private DKSRef source;
  private String msgId;
  private DKSObject payload = null; // invariant: only used if type==LookupType.DATA
  private boolean internal;

  public String getName() {
    return NAME;
  }

  public LookupType getType() {
    return type;
  }

  public Vector getStack() {
    return stack;
  }

  public DKSRef getSource() {
    return source;
  }

  public String getMsgId() {
    return msgId;
  }

  public DKSObject getPayload() {
    return payload;
  }

  public boolean getInternal() {
    return internal;
  }

  // Stackhandling.

  // ForwardedMsgInterface

  public long getTarget() {
    return target;
  }

  public long getIntervalStart() {
    return intervalStart;
  }

  public void setIntervalStart(long s) {
    intervalStart = s;
  }

  public LookupMsg() {

  }

  /**
   * Warning! No defensive copying done for performance reasons!
   * @param target long target identifier to search for
   * @param start start of the interval of the pointer followed
   * @param type String currently either "ADDR" or "DATA"
   * @param stack Vector of visited nodes
   * @param source DKSRef originator
   * @param msgId String request identifier
   */
  public LookupMsg(long target, LookupType type, DKSRef source, DKSRef start,
                   String msgId, boolean intern) {

    this.target = target;
    this.type = type;
    this.source = source;
    this.msgId = msgId;
    this.internal = intern;
    stack.add(0, source);
    stack.add(0, start);
  }

  /* this version of the constructor has a DKSObject payload
      invariant: should only be used if type==LookupType.ASYNCDATA (or SYNCDATA), else above constructor should be used
   */
  public LookupMsg(long target, LookupType type, DKSRef source, DKSRef start,
                   DKSObject payload, String msgId, boolean intern) {

    this.target = target;
    this.type = type;
    this.msgId = msgId;
    this.payload = payload;
    this.internal = intern;
    stack.add(0, source);
    stack.add(0, start);
  }

  public void marshal() throws IOException {
    marshaler.addLong(target, "target");
    marshaler.addLong(intervalStart, "start");
    marshaler.addBool(internal, "internal");
    marshaler.addString(type.toString(), "ltype");

    marshaler.addBool(msgId == null ? false : true , "anyinsertid");
    if (msgId != null) {
      marshaler.addString(msgId, "insertid");
    }

    marshaler.addBool(payload == null ? false : true , "anypayload");
    if (payload != null) {
      marshaler.addDKSObject(payload, "payload");
    }

    marshaler.addDKSRefArray( (DKSRef[]) stack.toArray(new DKSRef[0]), "stack");


  }

  public void unmarshal() throws IOException {
    target = marshaler.remLong("target");
    intervalStart = marshaler.remLong("start");
    internal = marshaler.remBool("internal");
    type = LookupType.valueOf(marshaler.remString("ltype"));

    final boolean anyId = marshaler.remBool("anyinsertid");
    if (anyId) {
      msgId = marshaler.remString("insertid");
    }

    final boolean anyLoad = marshaler.remBool("anypayload");
    if (anyLoad) {
      payload = marshaler.remDKSObject("payload");
    }
    stack = new Vector(marshaler.remDKSRefArray("stack"));
  }
}
